use super::position::Position;
use super::position::Span;
use pest::Parser;
use pest_derive::Parser;

#[derive(Parser)]
#[grammar = "parser.pest"]
pub struct SysYParser;

pub struct Node<T> {
    pub node: Box<T>,
    pub span: Span,
}

impl<T> Node<T> {
    fn new(node: T, span: Span) -> Self {
        Self { node: Box::new(node), span }
    }
}

pub struct Program {
    pub comp_unit_list: Vec<Node<CompUnit>>,
}

pub enum CompUnit {
    Decl(Node<Decl>),
    FuncDef(Node<FuncDef>),
}

pub enum Decl {
    ConstDecl(Node<ConstDecl>),
    VarDecl(Node<VarDecl>),
}

pub struct ConstDecl {
    pub b_type: Node<BType>,
    pub const_def_list: Vec<Node<ConstDef>>,
}

pub enum BType {
    Int,
}

pub struct ConstDef {
    pub ident: Node<String>,
    pub const_exp_list: Vec<Node<ConstExp>>,
    pub const_init_val: Node<ConstInitVal>,
}

pub enum ConstInitVal {
    ConstExp(Node<ConstExp>),
    ConstInitValList(Vec<Node<ConstInitVal>>),
}

pub struct VarDecl {
    pub b_type: Node<BType>,
    pub var_def_list: Vec<Node<VarDef>>,
}

pub struct VarDef {
    pub ident: Node<String>,
    pub const_exp_list: Vec<Node<ConstExp>>,
    pub init_val: Option<Node<InitVal>>,
}

pub enum InitVal {
    Exp(Node<Exp>),
    InitValList(Vec<Node<InitVal>>),
}

pub struct FuncDef {
    pub func_type: Node<FuncType>,
    pub ident: Node<String>,
    pub func_f_params: Option<Node<FuncFParams>>,
    pub block: Node<Block>,
}

pub enum FuncType {
    Void,
    Int,
}

pub struct FuncFParams {
    pub func_f_param_list: Vec<Node<FuncFParam>>,
}

pub struct FuncFParam {
    pub b_type: Node<BType>,
    pub ident: Node<String>,
    pub exp_list: Option<Vec<Node<Exp>>>,
}

pub struct Block {
    pub block_item_list: Vec<Node<BlockItem>>,
}

pub enum BlockItem {
    Decl(Node<Decl>),
    Stmt(Node<Stmt>),
}

pub enum Stmt {
    Assign(Node<LVal>, Node<Exp>),
    Exp(Option<Node<Exp>>),
    Block(Node<Block>),
    If(Node<Cond>, Node<Stmt>, Option<Node<Stmt>>),
    While(Node<Cond>, Node<Stmt>),
    Break,
    Continue,
    Return(Option<Node<Exp>>),
}

pub enum ExpOp {
    Add,
    Sub,
    Mul,
    Div,
    Mod,
}

pub enum Exp {
    Primary(Node<PrimaryExp>),
    Inner(Node<Exp>, Node<ExpOp>, Node<Exp>),
    Unary(Node<UnaryOp>, Node<Exp>),
}

pub enum CondOp {
    Or,
    And,
    Eq,
    Neq,
    Lt,
    Gt,
    Le,
    Ge,
}

pub enum Cond {
    Exp(Node<Exp>),
    CondExp(Node<Cond>, Node<CondOp>, Node<Cond>),
}

pub struct LVal {
    pub ident: Node<String>,
    pub exp_list: Vec<Node<Exp>>,
}

pub enum UnaryOp {
    Add,
    Sub,
    Not,
}

pub enum PrimaryExp {
    Number(i64, Node<String>),
    FuncRParams(Node<String>, Option<Node<FuncRParams>>),
    LVal(Node<LVal>),
    Exp(Node<Exp>),
}

pub struct FuncRParams {
    pub exp_list: Vec<Node<Exp>>,
}

pub struct ConstExp {
    pub exp: Node<Exp>,
}

pub struct ParserError {
    pub message: String,
    pub position: Position,
}

impl Display for ParserError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "Error type B at Line {}:{}",
            self.position.line, self.message
        )
    }
}

pub fn parse(input: &str) -> Result<Program, Vec<ParserError>> {
    let mut error_list: Vec<ParserError> = Vec::new();
    let mut pair = match SysYParser::parse(Rule::program, input) {
        Ok(o) => o,
        Err(e) => {
            let position = match e.line_col {
                pest::error::LineColLocation::Pos(position) => position,
                pest::error::LineColLocation::Span(position, _) => position,
            };
            error_list.push(ParserError {
                message: format!("{}", position.1),
                position: position.into(),
            });
            return Err(error_list);
        }
    };
    let pair = pair.next().unwrap();
    let program = visit_program(pair, &mut error_list);
    if error_list.len() > 0 {
        Err(error_list)
    } else {
        Ok(program)
    }
}

fn visit_program(program: pest::iterators::Pair<Rule>, error_list: &mut Vec<ParserError>) -> Program {
    let mut comp_unit_list: Vec<Node<CompUnit>> = Vec::new();
    for comp_unit in program.into_inner() {
        match comp_unit.as_rule() {
            Rule::comp_unit => {
                if let Ok(o) = visit_comp_unit(comp_unit, error_list) {
                    comp_unit_list.push(o);
                }
            }
            Rule::EOI => (),
            _ => unreachable!(),
        }
    }
    Program { comp_unit_list }
}

fn visit_comp_unit(comp_unit: pest::iterators::Pair<Rule>, error_list: &mut Vec<ParserError>) -> Result<Node<CompUnit>, ()> {
    let inner = comp_unit.into_inner().next().unwrap();
    let span = inner.as_span();
    match inner.as_rule() {
        Rule::decl => Ok(Node::new(CompUnit::Decl(visit_decl(inner, error_list)?), span.into())),
        Rule::func_def => Ok(Node::new(CompUnit::FuncDef(visit_func_def(inner, error_list)?), span.into())),
        Rule::block_recovery => {
            let position = inner.as_span().start_pos().line_col();
            error_list.push(ParserError { message: "comp_unit error".into(), position: position.into() });
            Err(())
        },
        Rule::comp_unit_ignore_end => Err(()),
        _ => unreachable!(),
    }
}

fn visit_decl(decl: pest::iterators::Pair<Rule>, error_list: &mut Vec<ParserError>) -> Result<Node<Decl>, ()> {
    let inner = decl.into_inner().next().unwrap();
    let span = inner.as_span();
    match inner.as_rule() {
        Rule::const_decl => Ok(Node::new(Decl::ConstDecl(visit_const_decl(inner, error_list)?), span.into())),
        Rule::var_decl => Ok(Node::new(Decl::VarDecl(visit_var_decl(inner, error_list)?), span.into())),
        _ => unreachable!(),
    }
}

fn visit_const_decl(const_decl: pest::iterators::Pair<Rule>, error_list: &mut Vec<ParserError>) -> Result<ConstDecl>, ()> {
    let mut b_type: Option<BType> = None;
    let mut const_def_list: Vec<ConstDef> = Vec::new();
    for inner in const_decl.into_inner() {
        match inner.as_rule() {
            Rule::r#const => (),
            Rule::b_type => b_type = Some(visit_b_type(inner)),
            Rule::const_def => const_def_list.push(visit_const_def(inner)),
            Rule::comma => (),
            Rule::semicolon => (),
            Rule::semicolon_error => {
                let position = inner.line_col();
                error_list.push(ParserError { message: "';' error".into(), position: position.into() });
                return Err(());
            }
            _ => unreachable!(),
        }
    }
    let b_type = b_type.unwrap();
    Ok(ConstDecl {
        b_type,
        const_def_list,
    })
}

fn visit_b_type(b_type: pest::iterators::Pair<Rule>) -> BType {
    let inner = b_type.into_inner().next().unwrap();
    match inner.as_rule() {
        Rule::int => BType::Int,
        _ => unreachable!(),
    }
}

fn visit_const_def(const_def: pest::iterators::Pair<Rule>) -> ConstDef {
    let mut ident: Option<String> = None;
    let mut const_exp_list: Vec<ConstExp> = Vec::new();
    let mut const_init_val: Option<ConstInitVal> = None;
    for inner in const_def.into_inner() {
        match inner.as_rule() {
            Rule::ident => ident = Some(inner.as_str().to_string()),
            Rule::l_brackt => (),
            Rule::const_exp => const_exp_list.push(visit_const_exp(inner)),
            Rule::r_brackt => (),
            Rule::assign => (),
            Rule::const_init_val => const_init_val = Some(visit_const_init_val(inner)),
            _ => unreachable!(),
        }
    }
    let ident = ident.unwrap();
    let const_init_val = const_init_val.unwrap();
    ConstDef {
        ident,
        const_exp_list,
        const_init_val,
    }
}

fn visit_const_init_val(const_init_val: pest::iterators::Pair<Rule>) -> ConstInitVal {
    let mut const_init_val_list: Vec<ConstInitVal> = Vec::new();
    for inner in const_init_val.into_inner() {
        match inner.as_rule() {
            Rule::const_exp => return ConstInitVal::ConstExp(visit_const_exp(inner)),
            Rule::l_brace => (),
            Rule::r_brace => (),
            Rule::const_init_val => const_init_val_list.push(visit_const_init_val(inner)),
            Rule::comma => (),
            _ => unreachable!(),
        }
    }
    ConstInitVal::ConstInitValList(const_init_val_list)
}

fn visit_var_decl(var_decl: pest::iterators::Pair<Rule>, error_list: &mut Vec<ParserError>) -> Result<VarDecl, ()> {
    let mut b_type: Option<BType> = None;
    let mut var_def_list: Vec<VarDef> = Vec::new();
    for inner in var_decl.into_inner() {
        match inner.as_rule() {
            Rule::b_type => b_type = Some(visit_b_type(inner)),
            Rule::var_def => var_def_list.push(visit_var_def(inner)),
            Rule::comma => (),
            Rule::semicolon => (),
            Rule::semicolon_error => {
                let position = inner.as_span().start_pos().line_col();
                error_list.push(ParserError { message: "';' error".into(), position: position.into() });
                return Err(());
            }
            _ => unreachable!(),
        }
    }
    let b_type = b_type.unwrap();
    Ok(VarDecl {
        b_type,
        var_def_list,
    })
}

fn visit_var_def(var_def: pest::iterators::Pair<Rule>) -> VarDef {
    let mut ident: Option<String> = None;
    let mut const_exp_list: Vec<ConstExp> = Vec::new();
    let mut init_val: Option<InitVal> = None;
    for inner in var_def.into_inner() {
        match inner.as_rule() {
            Rule::ident => ident = Some(inner.as_str().to_string()),
            Rule::l_brackt => (),
            Rule::const_exp => const_exp_list.push(visit_const_exp(inner)),
            Rule::r_brackt => (),
            Rule::assign => (),
            Rule::init_val => init_val = Some(visit_init_val(inner)),
            _ => unreachable!(),
        }
    }
    let ident = ident.unwrap();
    VarDef {
        ident,
        const_exp_list,
        init_val,
    }
}

fn visit_init_val(init_val: pest::iterators::Pair<Rule>) -> InitVal {
    let mut init_val_list: Vec<InitVal> = Vec::new();
    for inner in init_val.into_inner() {
        match inner.as_rule() {
            Rule::exp => return InitVal::Exp(visit_exp(inner)),
            Rule::l_brace => (),
            Rule::init_val => init_val_list.push(visit_init_val(inner)),
            Rule::comma => (),
            Rule::r_brace => (),
            _ => unreachable!(),
        }
    }
    InitVal::InitValList(init_val_list)
}

fn visit_func_def(func_def: pest::iterators::Pair<Rule>, error_list: &mut Vec<ParserError>) -> Result<FuncDef, ()> {
    let mut func_type: Option<FuncType> = None;
    let mut ident: Option<String> = None;
    let mut func_f_params: Option<FuncFParams> = None;
    let mut block: Option<Block> = None;
    for inner in func_def.into_inner() {
        match inner.as_rule() {
            Rule::func_type => func_type = Some(visit_func_type(inner)),
            Rule::ident => ident = Some(inner.as_str().into()),
            Rule::l_paren => (),
            Rule::func_f_params => func_f_params = Some(visit_func_f_params(inner)),
            Rule::r_paren => (),
            Rule::block => block = Some(visit_block(inner, error_list)?),
            _ => unreachable!(),
        }
    }
    let func_type = func_type.unwrap();
    let ident = ident.unwrap();
    let block = block.unwrap();
    Ok(FuncDef {
        func_type,
        ident,
        func_f_params,
        block,
    })
}

fn visit_func_type(func_type: pest::iterators::Pair<Rule>) -> FuncType {
    let inner = func_type.into_inner().next().unwrap();
    match inner.as_rule() {
        Rule::void => FuncType::Void,
        Rule::int => FuncType::Int,
        _ => unreachable!(),
    }
}

fn visit_func_f_params(func_f_params: pest::iterators::Pair<Rule>) -> FuncFParams {
    let mut func_f_param_list: Vec<FuncFParam> = Vec::new();
    for inner in func_f_params.into_inner() {
        match inner.as_rule() {
            Rule::func_f_param => func_f_param_list.push(visit_func_f_param(inner)),
            Rule::comma => (),
            _ => unreachable!(),
        }
    }
    FuncFParams { func_f_param_list }
}

fn visit_func_f_param(func_f_param: pest::iterators::Pair<Rule>) -> FuncFParam {
    let mut b_type: Option<BType> = None;
    let mut ident: Option<String> = None;
    let mut exp_list: Option<Vec<Exp>> = None;
    for inner in func_f_param.into_inner() {
        match inner.as_rule() {
            Rule::b_type => b_type = Some(visit_b_type(inner)),
            Rule::ident => ident = Some(inner.as_str().into()),
            Rule::l_brackt => {
                if exp_list.is_none() {
                    exp_list = Some(Vec::new());
                }
            }
            Rule::r_brackt => (),
            Rule::exp => exp_list.as_mut().unwrap().push(visit_exp(inner)),
            _ => unreachable!(),
        }
    }
    let b_type = b_type.unwrap();
    let ident = ident.unwrap();
    FuncFParam {
        b_type,
        ident,
        exp_list,
    }
}

fn visit_block(block: pest::iterators::Pair<Rule>, error_list: &mut Vec<ParserError>) -> Result<Block, ()> {
    let mut block_item_list: Vec<BlockItem> = Vec::new();
    for inner in block.into_inner() {
        match inner.as_rule() {
            Rule::l_brace => (),
            Rule::block_item => {
                if let Ok(o) = visit_block_item(inner, error_list) {
                    block_item_list.push(o);
                }
            }
            Rule::r_brace => (),
            Rule::r_brace_error => {
                let position = inner.line_col();
                error_list.push(ParserError { message: "'}' error".into(), position: position.into() });
                return Err(());
            }
            _ => unreachable!(),
        }
    }
    Ok(Block { block_item_list })
}

fn visit_block_item(block_item: pest::iterators::Pair<Rule>, error_list: &mut Vec<ParserError>) -> Result<BlockItem, ()> {
    let inner = block_item.into_inner().next().unwrap();
    match inner.as_rule() {
        Rule::decl => Ok(BlockItem::Decl(visit_decl(inner, error_list)?)),
        Rule::stmt => Ok(BlockItem::Stmt(visit_stmt(inner, error_list)?)),
        Rule::block_recovery => {
            let position = inner.as_span().start_pos().line_col();
            error_list.push(ParserError { message: "".into(), position: position.into() });
            Err(())
        },
        _ => unreachable!(),
    }
}

fn visit_stmt(stmt: pest::iterators::Pair<Rule>, error_list: &mut Vec<ParserError>) -> Result<Stmt, ()> {
    enum State {
        LVal,
        Exp,
        Block,
        If,
        While,
        Break,
        Continue,
        Return,
    }
    let mut state: State = State::Exp;
    let mut l_val: Option<LVal> = None;
    let mut exp: Option<Exp> = None;
    let mut block: Option<Block> = None;
    let mut cond: Option<Cond> = None;
    let mut other_stmt: Vec<Stmt> = Vec::new();
    for inner in stmt.into_inner() {
        match inner.as_rule() {
            Rule::l_val => {
                state = State::LVal;
                l_val = Some(visit_l_val(inner));
            }
            Rule::assign => (),
            Rule::exp => exp = Some(visit_exp(inner)),
            Rule::semicolon => (),
            Rule::block => {
                state = State::Block;
                block = Some(visit_block(inner, error_list)?);
            }
            Rule::r#if => state = State::If,
            Rule::l_paren => (),
            Rule::cond => cond = Some(visit_cond(inner)),
            Rule::r_paren => (),
            Rule::stmt => other_stmt.push(visit_stmt(inner, error_list)?),
            Rule::r#else => (),
            Rule::r#while => state = State::While,
            Rule::r#break => state = State::Break,
            Rule::r#continue => state = State::Continue,
            Rule::r#return => state = State::Return,
            _ => unreachable!(),
        }
    }
    Ok(match state {
        State::LVal => Stmt::Assign(l_val.unwrap(), exp.unwrap()),
        State::Exp => Stmt::Exp(exp),
        State::Block => Stmt::Block(block.unwrap()),
        State::If => {
            let (stmt0, stmt1) = match other_stmt.len() {
                1 => (other_stmt.pop().unwrap(), None),
                2 => (
                    other_stmt.remove(0),
                    Some(Box::new(other_stmt.pop().unwrap())),
                ),
                _ => unreachable!(),
            };
            Stmt::If(cond.unwrap(), Box::new(stmt0), stmt1)
        }
        State::While => Stmt::While(cond.unwrap(), Box::new(other_stmt.pop().unwrap())),
        State::Break => Stmt::Break,
        State::Continue => Stmt::Continue,
        State::Return => Stmt::Return(exp),
    })
}

fn visit_exp(exp: pest::iterators::Pair<Rule>) -> Exp {
    let inner = exp.into_inner().next().unwrap();
    match inner.as_rule() {
        Rule::add_exp => visit_add_exp(inner),
        _ => unreachable!(),
    }
}

fn visit_cond(cond: pest::iterators::Pair<Rule>) -> Cond {
    let inner = cond.into_inner().next().unwrap();
    match inner.as_rule() {
        Rule::l_or_exp => visit_l_or_exp(inner),
        _ => unreachable!(),
    }
}

fn visit_l_val(l_val: pest::iterators::Pair<Rule>) -> LVal {
    let mut ident: Option<String> = None;
    let mut exp_list: Vec<Exp> = Vec::new();
    for inner in l_val.into_inner() {
        match inner.as_rule() {
            Rule::ident => ident = Some(inner.as_str().into()),
            Rule::l_brackt => (),
            Rule::exp => exp_list.push(visit_exp(inner)),
            Rule::r_brackt => (),
            _ => unreachable!(),
        }
    }
    let ident = ident.unwrap();
    LVal { ident, exp_list }
}

fn visit_primary_exp(primary_exp: pest::iterators::Pair<Rule>) -> PrimaryExp {
    let mut exp: Option<PrimaryExp> = None;
    for inner in primary_exp.into_inner() {
        match inner.as_rule() {
            Rule::l_paren => (),
            Rule::exp => exp = Some(PrimaryExp::Exp(Box::new(visit_exp(inner)))),
            Rule::r_paren => (),
            Rule::l_val => return PrimaryExp::LVal(visit_l_val(inner)),
            Rule::number => {
                let (num, str) = visit_number(inner);
                return PrimaryExp::Number(num, str);
            }
            _ => unreachable!(),
        }
    }
    exp.unwrap()
}

fn visit_number(number: pest::iterators::Pair<Rule>) -> (i64, String) {
    let inner = number.into_inner().next().unwrap();
    match inner.as_rule() {
        Rule::integer_const => (parse_int(inner.as_str()).unwrap(), inner.as_str().to_string()),
        _ => unreachable!(),
    }
}

fn visit_unary_exp(unary_exp: pest::iterators::Pair<Rule>) -> Exp {
    enum State {
        Ident,
        Unary,
    }
    let mut state = State::Ident;
    let mut ident: Option<String> = None;
    let mut func_r_params: Option<FuncRParams> = None;
    let mut unary_op: Option<UnaryOp> = None;
    let mut other_unary_exp: Option<Exp> = None;
    for inner in unary_exp.into_inner() {
        match inner.as_rule() {
            Rule::ident => ident = Some(inner.as_str().to_string()),
            Rule::l_paren => (),
            Rule::func_r_params => func_r_params = Some(visit_func_r_params(inner)),
            Rule::r_paren => (),
            Rule::primary_exp => return Exp::Primary(visit_primary_exp(inner)),
            Rule::unary_op => {
                state = State::Unary;
                unary_op = Some(visit_unary_op(inner));
            }
            Rule::unary_exp => other_unary_exp = Some(visit_unary_exp(inner)),
            _ => unreachable!(),
        }
    }
    match state {
        State::Ident => Exp::Primary(PrimaryExp::FuncRParams(ident.unwrap(), func_r_params)),
        State::Unary => Exp::Unary(unary_op.unwrap(), Box::new(other_unary_exp.unwrap())),
    }
}

fn visit_unary_op(unary_op: pest::iterators::Pair<Rule>) -> UnaryOp {
    let inner = unary_op.into_inner().next().unwrap();
    match inner.as_rule() {
        Rule::plus => UnaryOp::Add,
        Rule::minus => UnaryOp::Sub,
        Rule::not => UnaryOp::Not,
        _ => unreachable!(),
    }
}

fn visit_func_r_params(func_r_params: pest::iterators::Pair<Rule>) -> FuncRParams {
    let mut exp_list: Vec<Exp> = Vec::new();
    for inner in func_r_params.into_inner() {
        match inner.as_rule() {
            Rule::exp => exp_list.push(visit_exp(inner)),
            Rule::comma => (),
            _ => unreachable!(),
        }
    }
    FuncRParams { exp_list }
}

fn visit_mul_exp(mul_exp: pest::iterators::Pair<Rule>) -> Exp {
    let mut unary_exp_list: Vec<Exp> = Vec::new();
    let mut exp_op_list: Vec<ExpOp> = Vec::new();
    for inner in mul_exp.into_inner() {
        match inner.as_rule() {
            Rule::unary_exp => unary_exp_list.push(visit_unary_exp(inner)),
            Rule::mul => exp_op_list.push(ExpOp::Mul),
            Rule::div => exp_op_list.push(ExpOp::Div),
            Rule::r#mod => exp_op_list.push(ExpOp::Mod),
            _ => unreachable!(),
        }
    }
    unary_exp_list.reverse();
    exp_op_list.reverse();
    assert_eq!(unary_exp_list.len() - 1, exp_op_list.len());
    while unary_exp_list.len() > 1 {
        let l_exp = unary_exp_list.pop().unwrap();
        let op = exp_op_list.pop().unwrap();
        let r_exp = unary_exp_list.pop().unwrap();
        let exp = Exp::Inner(Box::new(l_exp), op, Box::new(r_exp));
        unary_exp_list.push(exp);
    }
    unary_exp_list.pop().unwrap()
}

fn visit_add_exp(add_exp: pest::iterators::Pair<Rule>) -> Exp {
    let mut mul_exp_list: Vec<Exp> = Vec::new();
    let mut exp_op_list: Vec<ExpOp> = Vec::new();
    for inner in add_exp.into_inner() {
        match inner.as_rule() {
            Rule::mul_exp => mul_exp_list.push(visit_mul_exp(inner)),
            Rule::plus => exp_op_list.push(ExpOp::Add),
            Rule::minus => exp_op_list.push(ExpOp::Sub),
            _ => unreachable!(),
        }
    }
    mul_exp_list.reverse();
    exp_op_list.reverse();
    assert_eq!(mul_exp_list.len() - 1, exp_op_list.len());
    while mul_exp_list.len() > 1 {
        let l_exp = mul_exp_list.pop().unwrap();
        let op = exp_op_list.pop().unwrap();
        let r_exp = mul_exp_list.pop().unwrap();
        let exp = Exp::Inner(Box::new(l_exp), op, Box::new(r_exp));
        mul_exp_list.push(exp);
    }
    mul_exp_list.pop().unwrap()
}

fn visit_rel_exp(rel_exp: pest::iterators::Pair<Rule>) -> Cond {
    let mut exp_list: Vec<Cond> = Vec::new();
    let mut op_list: Vec<CondOp> = Vec::new();
    for inner in rel_exp.into_inner() {
        match inner.as_rule() {
            Rule::add_exp => exp_list.push(Cond::Exp(visit_add_exp(inner))),
            Rule::lt => op_list.push(CondOp::Lt),
            Rule::gt => op_list.push(CondOp::Gt),
            Rule::le => op_list.push(CondOp::Le),
            Rule::ge => op_list.push(CondOp::Ge),
            _ => unreachable!(),
        }
    }
    exp_list.reverse();
    op_list.reverse();
    assert_eq!(exp_list.len() - 1, op_list.len());
    while exp_list.len() > 1 {
        let l_exp = exp_list.pop().unwrap();
        let op = op_list.pop().unwrap();
        let r_exp = exp_list.pop().unwrap();
        let exp = Cond::CondExp(Box::new(l_exp), op, Box::new(r_exp));
        exp_list.push(exp);
    }
    exp_list.pop().unwrap()
}

fn visit_eq_exp(eq_exp: pest::iterators::Pair<Rule>) -> Cond {
    let mut exp_list: Vec<Cond> = Vec::new();
    let mut op_list: Vec<CondOp> = Vec::new();
    for inner in eq_exp.into_inner() {
        match inner.as_rule() {
            Rule::rel_exp => exp_list.push(visit_rel_exp(inner)),
            Rule::eq => op_list.push(CondOp::Eq),
            Rule::neq => op_list.push(CondOp::Neq),
            _ => unreachable!(),
        }
    }
    exp_list.reverse();
    op_list.reverse();
    assert_eq!(exp_list.len() - 1, op_list.len());
    while exp_list.len() > 1 {
        let l_exp = exp_list.pop().unwrap();
        let op = op_list.pop().unwrap();
        let r_exp = exp_list.pop().unwrap();
        let exp = Cond::CondExp(Box::new(l_exp), op, Box::new(r_exp));
        exp_list.push(exp);
    }
    exp_list.pop().unwrap()
}

fn visit_l_and_exp(l_and_exp: pest::iterators::Pair<Rule>) -> Cond {
    let mut exp_list: Vec<Cond> = Vec::new();
    let mut op_list: Vec<CondOp> = Vec::new();
    for inner in l_and_exp.into_inner() {
        match inner.as_rule() {
            Rule::eq_exp => exp_list.push(visit_eq_exp(inner)),
            Rule::and => op_list.push(CondOp::And),
            _ => unreachable!(),
        }
    }
    exp_list.reverse();
    op_list.reverse();
    assert_eq!(exp_list.len() - 1, op_list.len());
    while exp_list.len() > 1 {
        let l_exp = exp_list.pop().unwrap();
        let op = op_list.pop().unwrap();
        let r_exp = exp_list.pop().unwrap();
        let exp = Cond::CondExp(Box::new(l_exp), op, Box::new(r_exp));
        exp_list.push(exp);
    }
    exp_list.pop().unwrap()
}

fn visit_l_or_exp(l_or_exp: pest::iterators::Pair<Rule>) -> Cond {
    let mut exp_list: Vec<Cond> = Vec::new();
    let mut op_list: Vec<CondOp> = Vec::new();
    for inner in l_or_exp.into_inner() {
        match inner.as_rule() {
            Rule::l_and_exp => exp_list.push(visit_l_and_exp(inner)),
            Rule::or => op_list.push(CondOp::Or),
            _ => unreachable!(),
        }
    }
    exp_list.reverse();
    op_list.reverse();
    while exp_list.len() > 1 {
        let l_exp = exp_list.pop().unwrap();
        let op = op_list.pop().unwrap();
        let r_exp = exp_list.pop().unwrap();
        let exp = Cond::CondExp(Box::new(l_exp), op, Box::new(r_exp));
        exp_list.push(exp);
    }
    exp_list.pop().unwrap()
}

fn visit_const_exp(const_exp: pest::iterators::Pair<Rule>) -> ConstExp {
    let inner = const_exp.into_inner().next().unwrap();
    let exp = match inner.as_rule() {
        Rule::add_exp => visit_add_exp(inner),
        _ => unreachable!(),
    };
    ConstExp { exp }
}

use std::{fmt::Display, num::ParseIntError};
fn parse_int(s: &str) -> Result<i64, ParseIntError> {
    let s = s.trim();

    if let Some(hex) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) {
        return i64::from_str_radix(hex, 16);
    } else if let Some(bin) = s.strip_prefix("0b").or_else(|| s.strip_prefix("0B")) {
        return i64::from_str_radix(bin, 2);
    } else if let Some(oct) = s.strip_prefix("0") {
        if oct.is_empty() {
            return Ok(0);
        }
        return i64::from_str_radix(oct, 8);
    }
    s.parse::<i64>()
}
